package cn.angis.biz.flow.service;

import cn.angis.biz.flow.dto.input.LeaveInput;
import cn.angis.biz.flow.dto.output.LeaveOutput;
import cn.angis.biz.flow.dtomapper.LeaveStruct;
import cn.angis.biz.flow.mapper.LeaveMapper;
import cn.angis.biz.flow.model.Leave;
import cn.angis.common.constant.Constant;
import cn.angis.common.exceprion.ServiceException;
import cn.angis.common.util.IdGen;
import cn.angis.common.util.StringU;
import cn.angis.db.service.BaseService;
import cn.angis.system.dto.output.LoginOutput;
import cn.angis.system.model.Role;
import cn.angis.system.service.RoleService;
import cn.angis.system.util.AuthUtil;
import org.beetl.sql.clazz.kit.AutoSQLEnum;
import org.beetl.sql.core.BaseSQLExecutor;
import org.beetl.sql.core.SQLExecutor;
import org.dromara.warm.flow.core.FlowEngine;
import org.dromara.warm.flow.core.dto.FlowParams;
import org.dromara.warm.flow.core.entity.Instance;
import org.dromara.warm.flow.core.entity.Task;
import org.dromara.warm.flow.core.entity.User;
import org.dromara.warm.flow.core.enums.SkipType;
import org.dromara.warm.flow.core.service.InsService;
import org.dromara.warm.flow.core.service.TaskService;
import org.noear.snack.ONode;
import org.noear.solon.annotation.Component;
import org.noear.solon.annotation.Inject;
import org.noear.solon.data.annotation.Transaction;

import java.util.*;
import java.util.stream.Collectors;

/**
* OA 请假申请表服务
* @author angis.cn
* @Date 2025-06-14
*/
@Component
public class LeaveService extends BaseService<LeaveMapper, Leave> {

    @Inject
    private RoleService roleService;

    @Inject
    private TaskService taskService;

    @Inject
    private InsService insService;

    public List<LeaveOutput> leaveList(LeaveInput leaveInput) {
        return baseMapper.selectList(leaveInput);
    }

    public int insert(LeaveInput leaveInput) {
        // 设置流转参数
        String id = IdGen.getId();
        leaveInput.setId(id);
        LoginOutput user = AuthUtil.getUser();
        // 从字典表中获取流程编码
        String flowCode = "leaveFlow-meet-sign";
        // 传递流程编码，绑定流程定义 【必传】
        FlowParams flowParams = FlowParams.build().flowCode(flowCode);
        // 设置办理人唯一标识，保存为流程实例的创建人 【必传】
        flowParams.handler(user.getId());
        // 流程变量
        Map<String, Object> variable = new HashMap<>();
        // 流程变量传递业务数据，按实际业务需求传递 【按需传】
        variable.put("businessData", leaveInput);
        variable.put("businessType", "testLeave");
        // 条件表达式替换，判断是否满足某个任务的跳转条件  【按需传】
        variable.put("flag", String.valueOf(leaveInput.getDay()));
        // 办理人表达式替换  【按需传】
        variable.put("handler1", Arrays.asList(4, "5", 100L));
        variable.put("handler2", 12L);
        variable.put("handler3", new Object[] {9, "10", 102L});
        variable.put("handler4", "15");
        Task task = FlowEngine.newTask().setId(55L);
        variable.put("handler5", task);
        variable.put("handler6", 77L);

        flowParams.variable(variable);
        // 自定义流程状态扩展
        if (StringU.isNotEmpty(leaveInput.getNewFlowStatus())) {
            flowParams.flowStatus(leaveInput.getNewFlowStatus()).hisStatus(leaveInput.getNewFlowStatus());
        }

        // 新增请假表
        Instance instance = insService.start(id, flowParams);
        leaveInput.setInstanceId(instance.getId());
        leaveInput.setNodeCode(instance.getNodeCode());
        leaveInput.setNodeName(instance.getNodeName());
        leaveInput.setNodeType(instance.getNodeType());
        leaveInput.setFlowStatus(instance.getFlowStatus());
        leaveInput.setCreateDate(new Date());
        leaveInput.setCreateBy(user.getUsername());
        leaveInput.setDelFlag(Constant.ENABLE);
        // 新增抄送人方法  【按需】
        if (StringU.isNotNull(leaveInput.getAdditionalHandler())) {
            List<User> users = FlowEngine.userService().structureUser(instance.getId()
                    , leaveInput.getAdditionalHandler(), "4");
            FlowEngine.userService().saveBatch(users);
        }
        // 此处可以发送消息通知，比如短信通知，邮件通知等，代码自己实现
        Leave leave = LeaveStruct.INSTANCE.toLeave(leaveInput);
        return baseMapper.insert(leave);
    }

    @Transaction
    public int submit(LeaveInput leaveInput) {
        // 设置流转参数
        Leave leave = getById(leaveInput.getId());
        LoginOutput user = AuthUtil.getUser();
        // 是通过流程还是退回流程  【必传】
        FlowParams flowParams = FlowParams.build().skipType(SkipType.PASS.getKey());
        // 作为办理人保存到历史记录表 【必传】
        flowParams.handler(user.getId());
        // 设置办理人拥有的权限，办理中需要校验是否有权限办理 【必传】
        List<Role> roles = user.getRoles();
        List<String> permissionList = new ArrayList<>();
        if (Objects.nonNull(roles)) {
            permissionList = roles.stream().map(role -> "role:" + role.getId()).collect(Collectors.toList());
        }
        permissionList.add("dept:" + user.getDeptId());
        permissionList.add(user.getId());
        flowParams.permissionFlag(permissionList);
        // 自定义流程状态扩展  【按需传】
        if (StringU.isNotEmpty(leaveInput.getNewFlowStatus())) {
            flowParams.flowStatus(leaveInput.getNewFlowStatus()).hisStatus(leaveInput.getNewFlowStatus());
        }
        // 流程变量
        Map<String, Object> variable = new HashMap<>();
        // 流程变量传递业务数据，按实际业务需求传递  【按需传】
        variable.put("businessType", "testLeave");
        // 办理人表达式替换  【按需传】
        variable.put("flag", String.valueOf(leave.getDay()));
        flowParams.variable(variable);

        // 更新请假表
        Instance instance = taskService.skipByInsId(leave.getInstanceId(), flowParams);
        leave.setNodeCode(instance.getNodeCode());
        leave.setNodeName(instance.getNodeName());
        leave.setNodeType(instance.getNodeType());
        leave.setFlowStatus(instance.getFlowStatus());
        leave.setUpdateDate(new Date());
        return update(leave);
    }

    @Transaction
    public int handle(LeaveInput leaveInput) {
        // 设置流转参数
        LoginOutput user = AuthUtil.getUser();
        // 是通过流程还是退回流程 【必传】
        FlowParams flowParams = FlowParams.build().skipType(leaveInput.getSkipType());
        // 作为办理人保存到历史记录表 【必传】
        flowParams.handler(user.getId());
        // 如果需要任意跳转流程，传入此参数  【按需传】
        flowParams.nodeCode(leaveInput.getNewNodeCode());
        // 作为审批意见保存到历史记录表  【按需传】
        flowParams.message(leaveInput.getMessage());

        // 设置办理人拥有的权限，办理中需要校验是否有权限办理 【必传】
        List<Role> roles = user.getRoles();
        List<String> permissionList = new ArrayList<>();
        if (Objects.nonNull(roles)) {
            permissionList = roles.stream().map(role -> "role:" + role.getId()).collect(Collectors.toList());
        }
        permissionList.add("dept:" + user.getDeptId());
        permissionList.add(user.getId());
        flowParams.permissionFlag(permissionList);

        // 流程变量
        Map<String, Object> variable = new HashMap<>();
        // 流程变量传递业务数据，按实际业务需求传递  【按需传】
        variable.put("businessType", "testLeave");
        // 办理人表达式替换  【按需传】
        variable.put("flag", String.valueOf(leaveInput.getDay()));
        flowParams.variable(variable);
        // 自定义流程状态扩展  【按需传】
        if (StringU.isNotEmpty(leaveInput.getNewFlowStatus())) {
            flowParams.flowStatus(leaveInput.getNewFlowStatus()).hisStatus(leaveInput.getNewFlowStatus());
        }
        // 请假信息存入flowParams,方便查看历史审批数据  【按需传】
        Leave leave = LeaveStruct.INSTANCE.toLeave(leaveInput);
        flowParams.hisTaskExt(ONode.serialize(leave));
        Instance instance = taskService.skip(leaveInput.getTaskId(), flowParams);

        // 更新请假表
        leave.setNodeCode(instance.getNodeCode());
        leave.setNodeName(instance.getNodeName());
        leave.setNodeType(instance.getNodeType());
        leave.setFlowStatus(instance.getFlowStatus());
        return update(leave);
    }

    @Transaction
    public int rejectLast(LeaveInput leaveInput) {
        // 设置流转参数
        LoginOutput user = AuthUtil.getUser();
        FlowParams flowParams = FlowParams.build();
        // 作为办理人保存到历史记录表 【必传】
        flowParams.handler(user.getId());
        // 作为审批意见保存到历史记录表  【按需传】
        flowParams.message(leaveInput.getMessage());

        // 设置办理人拥有的权限，办理中需要校验是否有权限办理 【必传】
        List<Role> roles = user.getRoles();
        List<String> permissionList = new ArrayList<>();
        if (Objects.nonNull(roles)) {
            permissionList = roles.stream().map(role -> "role:" + role.getId()).collect(Collectors.toList());
        }
        permissionList.add("dept:" + user.getDeptId());
        permissionList.add(user.getId());
        flowParams.permissionFlag(permissionList);

        // 流程变量
        Map<String, Object> variable = new HashMap<>();
        // 流程变量传递业务数据，按实际业务需求传递  【按需传】
        variable.put("businessType", "testLeave");
        // 办理人表达式替换  【按需传】
        variable.put("flag", String.valueOf(leaveInput.getDay()));
        flowParams.variable(variable);
        // 自定义流程状态扩展  【按需传】
        if (StringU.isNotEmpty(leaveInput.getNewFlowStatus())) {
            flowParams.flowStatus(leaveInput.getNewFlowStatus()).hisStatus(leaveInput.getNewFlowStatus());
        }
        // 请假信息存入flowParams,方便查看历史审批数据  【按需传】
        Leave testLeave = LeaveStruct.INSTANCE.toLeave(leaveInput);
        flowParams.hisTaskExt(ONode.serialize(testLeave));
        Instance instance = taskService.rejectLast(leaveInput.getTaskId(), flowParams);

        // 更新请假表
        testLeave.setNodeCode(instance.getNodeCode());
        testLeave.setNodeName(instance.getNodeName());
        testLeave.setNodeType(instance.getNodeType());
        testLeave.setFlowStatus(instance.getFlowStatus());
        return update(testLeave);
    }

    @Transaction
    public int taskBack(LeaveInput leaveInput) {
        // 设置流转参数
        LoginOutput user = AuthUtil.getUser();
        FlowParams flowParams = FlowParams.build();
        // 作为办理人保存到历史记录表 【必传】
        flowParams.handler(user.getId());
        // 作为审批意见保存到历史记录表  【按需传】
        flowParams.message(leaveInput.getMessage());

        // 设置办理人拥有的权限，办理中需要校验是否有权限办理 【必传】
        List<Role> roles = user.getRoles();
        List<String> permissionList = new ArrayList<>();
        if (Objects.nonNull(roles)) {
            permissionList = roles.stream().map(role -> "role:" + role.getId()).collect(Collectors.toList());
        }
        permissionList.add("dept:" + user.getDeptId());
        permissionList.add(user.getId());
        flowParams.permissionFlag(permissionList);

        // 流程变量
        Map<String, Object> variable = new HashMap<>();
        // 流程变量传递业务数据，按实际业务需求传递  【按需传】
        variable.put("businessType", "testLeave");
        // 办理人表达式替换  【按需传】
        variable.put("flag", String.valueOf(leaveInput.getDay()));
        flowParams.variable(variable);
        // 自定义流程状态扩展  【按需传】
        if (StringU.isNotEmpty(leaveInput.getNewFlowStatus())) {
            flowParams.flowStatus(leaveInput.getNewFlowStatus()).hisStatus(leaveInput.getNewFlowStatus());
        }
        // 请假信息存入flowParams,方便查看历史审批数据  【按需传】
        Leave testLeave = LeaveStruct.INSTANCE.toLeave(leaveInput);
        flowParams.hisTaskExt(ONode.serialize(testLeave));
        Instance instance = taskService.taskBack(leaveInput.getTaskId(), flowParams);

        // 更新请假表
        testLeave.setNodeCode(instance.getNodeCode());
        testLeave.setNodeName(instance.getNodeName());
        testLeave.setNodeType(instance.getNodeType());
        testLeave.setFlowStatus(instance.getFlowStatus());
        return update(testLeave);
    }

    @Transaction
    public int revoke(String id) {
        Leave testLeave = getById(id);
        // 设置流转参数
        LoginOutput user = AuthUtil.getUser();
        FlowParams flowParams = FlowParams.build();
        // 作为办理人保存到历史记录表 【必传】
        flowParams.handler(user.getId());
        // 作为审批意见保存到历史记录表  【按需传】
        flowParams.message("撤销流程");

        // 流程变量
        Map<String, Object> variable = new HashMap<>();
        // 流程变量传递业务数据，按实际业务需求传递  【按需传】
        variable.put("businessType", "testLeave");
        // 办理人表达式替换  【按需传】
        variable.put("flag", String.valueOf(testLeave.getDay()));
        flowParams.variable(variable);
        // 请假信息存入flowParams,方便查看历史审批数据  【按需传】
        flowParams.hisTaskExt(ONode.serialize(testLeave));
        Instance instance = taskService.revoke(testLeave.getInstanceId(), flowParams);

        // 更新请假表
        testLeave.setNodeCode(instance.getNodeCode());
        testLeave.setNodeName(instance.getNodeName());
        testLeave.setNodeType(instance.getNodeType());
        testLeave.setFlowStatus(instance.getFlowStatus());
        return update(testLeave);
    }

    @Transaction
    public int taskBackByInsId(String id) {
        Leave testLeave = getById(id);
        // 设置流转参数
        LoginOutput user = AuthUtil.getUser();
        FlowParams flowParams = FlowParams.build();
        // 作为办理人保存到历史记录表 【必传】
        flowParams.handler(user.getId());
        // 作为审批意见保存到历史记录表  【按需传】
        flowParams.message("撤销流程");

        // 流程变量
        Map<String, Object> variable = new HashMap<>();
        // 流程变量传递业务数据，按实际业务需求传递  【按需传】
        variable.put("businessType", "testLeave");
        // 办理人表达式替换  【按需传】
        variable.put("flag", String.valueOf(testLeave.getDay()));
        flowParams.variable(variable);
        // 请假信息存入flowParams,方便查看历史审批数据  【按需传】
        flowParams.hisTaskExt(ONode.serialize(testLeave));
        Instance instance = taskService.taskBackByInsId(testLeave.getInstanceId(), flowParams);

        // 更新请假表
        testLeave.setNodeCode(instance.getNodeCode());
        testLeave.setNodeName(instance.getNodeName());
        testLeave.setNodeType(instance.getNodeType());
        testLeave.setFlowStatus(instance.getFlowStatus());
        return update(testLeave);
    }

    public int termination(Leave testLeave) {
        // 设置流转参数
        FlowParams flowParams = new FlowParams();
        LoginOutput user = AuthUtil.getUser();
        // 作为审批意见保存到历史记录表  【按需传】
        flowParams.message("终止流程");
        // 作为办理人保存到历史记录表 【必传】
        flowParams.handler(user.getId());

        Map<String, Object> variable = new HashMap<>();
        // 流程变量传递业务数据，按实际业务需求传递  【按需传】
        variable.put("businessType", "testLeave");
        flowParams.variable(variable);

        Instance instance = taskService.terminationByInsId(testLeave.getInstanceId(), flowParams);
        if (instance == null) {
            throw new ServiceException("流程实例不存在");
        }

        // 更新请假表
        testLeave.setNodeCode(instance.getNodeCode());
        testLeave.setNodeName(instance.getNodeName());
        testLeave.setNodeType(instance.getNodeType());
        testLeave.setFlowStatus(instance.getFlowStatus());
        return update(testLeave);
    }
}